from __future__ import annotations
import ctypes
import json
from ctypes import wintypes
import win32gui
import win32process
import win32con
import uiautomation as auto
from typing import Optional
from dataclasses import dataclass
from typing import List
import re


@dataclass
class BBox:
    left: int
    top: int
    right: int
    bottom: int

    @property
    def width(self) -> int:
        return self.right - self.left

    @property
    def height(self) -> int:
        return self.bottom - self.right

    @property
    def width(self) -> int:
        return self.right - self.left

    @property
    def height(self) -> int:
        return self.bottom - self.top

    def xcenter(self) -> int:
        return self.left + self.width // 2

    @property
    def ycenter(self) -> int:
        return self.top + self.height // 2

    @property
    def isempty(self) -> int:
        return self.width == 0 or self.height == 0

    @property
    def contains(self, x: int, y: int) -> bool:
        return self.left <= x < self.right and self.top <= y < self.bottom


user32 = ctypes.windll.user32


class WindowInfo:
    def __init__(self, hwnd: int):
        self.hwnd = hwnd

    # ====== 基础 ======
    def title(self) -> str:
        return win32gui.GetWindowText(self.hwnd)

    def class_name(self) -> str:
        # Win32 class_name
        return win32gui.GetClassName(self.hwnd)

    def uia_class_name(self) -> str:
        try:
            ctrl = auto.ControlFromHandle(self.hwnd)
            return ctrl.ClassName
        except Exception as e:
            print(e)
            return ""

    def uia_control_type(self) -> str:
        try:
            ctrl = auto.ControlFromHandle(self.hwnd)
            return ctrl.ControlTypeName
        except Exception as e:
            print(e)
            return ""

    def uia_name(self) -> str:
        try:
            ctrl = auto.ControlFromHandle(self.hwnd)
            return ctrl.Name
        except Exception as e:
            print(e)
            return ""

    def pid_tid(self):
        tid, pid = win32process.GetWindowThreadProcessId(self.hwnd)
        return pid, tid

    def parent(self) -> int:
        return win32gui.GetParent(self.hwnd)

    def owner(self) -> int:
        return win32gui.GetWindow(self.hwnd, win32con.GW_OWNER)

    # ====== 状态 ======
    def is_visible(self) -> bool:
        return bool(user32.IsWindowVisible(self.hwnd))

    def is_enabled(self) -> bool:
        return bool(user32.IsWindowEnabled(self.hwnd))

    def is_minimized(self) -> bool:
        user32.IsIconic.argtypes = [wintypes.HWND]
        user32.IsIconic.restype = wintypes.BOOL
        return bool(user32.IsIconic(self.hwnd))

    def is_maximized(self) -> bool:
        user32.IsZoomed.argtypes = [wintypes.HWND]
        user32.IsZoomed.restype = wintypes.BOOL
        return bool(user32.IsZoomed(self.hwnd))

    # ====== 样式 ======
    def styles(self):
        style = win32gui.GetWindowLong(self.hwnd, win32con.GWL_STYLE)
        exstyle = win32gui.GetWindowLong(self.hwnd, win32con.GWL_EXSTYLE)
        return style, exstyle

    # ====== 窗口矩形 ======
    def rect(self) -> BBox:
        l, t, r, b = win32gui.GetWindowRect(self.hwnd)
        return BBox(left=l, top=t, right=r, bottom=b)

    def client_rect(self) -> BBox:
        l, t, r, b = win32gui.GetClientRect(self.hwnd)
        return BBox(left=l, top=t, right=r, bottom=b)

    # ====== 其它 ======
    def has_menu(self) -> bool:
        return win32gui.GetMenu(self.hwnd) != 0

    def z_order(self):
        prev_hwnd = win32gui.GetWindow(self.hwnd, win32con.GW_HWNDPREV)
        next_hwnd = win32gui.GetWindow(self.hwnd, win32con.GW_HWNDNEXT)
        return prev_hwnd, next_hwnd

    def children(self):
        """返回子窗口列表 (hwnd, title, class)"""
        result = []

        def callback(h, _):
            result.append((h, win32gui.GetWindowText(h), win32gui.GetClassName(h)))

        win32gui.EnumChildWindows(self.hwnd, callback, None)
        return result

    # ====== 汇总 ======
    def __str__(self):
        if self is not None:
            return json.dumps(self.to_dict(), ensure_ascii=False)

    def to_dict(self) -> dict:
        pid, tid = self.pid_tid()
        style, exstyle = self.styles()
        zprev, znext = self.z_order()

        rect = self.rect()
        client_rect = self.client_rect()
        return {
            "hwnd": self.hwnd,
            "title": self.title(),
            "class_name": self.class_name(),
            "uia_class_name": self.uia_class_name(),
            "uia_control_type": self.uia_control_type(),
            "uia_name": self.uia_name(),
            "pid": pid,
            "thread_id": tid,
            "parent_hwnd": self.parent(),
            "owner_hwnd": self.owner(),
            "is_visible": self.is_visible(),
            "is_enabled": self.is_enabled(),
            "is_minimized": self.is_minimized(),
            "is_maximized": self.is_maximized(),
            # "style": hex(style),
            # "exstyle": hex(exstyle),
            "rect": {
                "left": rect.left,
                "top": rect.top,
                "right": rect.right,
                "bottom": rect.bottom,
                "width": rect.width,
                "height": rect.height,
            },
            # "client_rect": {
            #     "left": client_rect.left,
            #     "top": client_rect.top,
            #     "right": client_rect.right,
            #     "bottom": client_rect.bottom,
            #     "width": client_rect.width,
            #     "height": client_rect.height,
            # },
            "has_menu": self.has_menu(),
            "z_order_prev": zprev,
            "z_order_next": znext,
            "children": self.children()
        }


# ====== 示例 ======
if __name__ == "__main__":
    hwnd = int(input("请输入窗口句柄 (hwnd): ").strip())
    winfo = WindowInfo(hwnd)
    for k, v in winfo.to_dict().items():
        print(f"{k}: {v}")
